package br.unb.core.traffic;

import java.net.DatagramPacket;
import java.net.InetAddress;

import cgl.narada.sync.SyncTimestamp;

/**
 * @author Renato Sales Bizerra e Bruno
 *
 * This class controls the datagram to be send in the traffic. This
 * datagram has a header of 12 bytes consisting of the timestamp of the time the
 * packet was generated and the sequence number of the packet. 
 * The organization of the packet is as follows:
 *
 *                          1
 *      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
 *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *     |   Timestamp   |  Nr.  |  TAM  |
 *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *     |        Carga do Pacote        |
 *     +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 */
public class TrafficDatagramPacket {

    /**UDP Packet*/
    private DatagramPacket trafficPacket;
    /**Timestamp Packet of NTP protocol*/
    private SyncTimestamp timestamp;
    /**Data contained inside the UDP Packets on the network*/
    private byte[] data;
    /**Sequence number and packet size*/
    private int packetNum, packetLen;

    /**
     * Executed on sender machine
     * @author Renato Sales e Bruno
     * @param ina - Destination address of this packet
     * @param port - Destination port
     * @param len - Total length of the UDP packet to be generated, min 12 bytes
     * @param numPacket - Sequence number of packet
     */
    public TrafficDatagramPacket(
            InetAddress ina,
            int port,
            int len,
            int numPacket,
            byte[] packetData)
            throws Exception {
        packetLen = len;
        data = new byte[len];
        packetNum = numPacket;
        System.arraycopy(packetData, 0, data, 16, len - 16);           //Copies the given data to the packet data
        System.arraycopy(intToByte(numPacket), 0, data, 8, 4);         //Inserts the sequence number of the packet into the header
        System.arraycopy(intToByte(len), 0, data, 12, 4);              //Inserts the the size of the packet into the header
        timestamp = new SyncTimestamp();                               //TimeStamp with the time right after the construction of the packet
        System.arraycopy(timestamp.getTimestampData(), 0, data, 0, 8); //Copies the timestamp to the packet header.
        trafficPacket = new DatagramPacket(data, len, ina, port);      //Assembles the packet
    }

    /**
     * Exeuted o receiver machine
     */
    public TrafficDatagramPacket(DatagramPacket receivePacket) {
        trafficPacket = receivePacket;
        data = trafficPacket.getData();
        timestamp = new SyncTimestamp(0, data);
        packetNum = this.byteToInt(8, data);
        packetLen = this.byteToInt(12, data);
    }

    /**
     * @return data Returns the data of this packet
     */
    public byte[] getData() {
        return data;
    }

    /**
     * @return Returns the packetNum.
     */
    public int getPacketNum() {
        return packetNum;
    }

    /**
     * @return Returns the packetLen.
     */
    public int getPacketLen() {
        return packetLen;
    }

    /**
     * @return Returns the timestamp.
     */
    public SyncTimestamp getTimestamp() {
        return timestamp;
    }

    /**
     * @return Returns the trafficPacket.
     */
    public DatagramPacket getTrafficPacket() {
        return trafficPacket;
    }

    /**
     * Transforma números inteiros em array de bytes
     * @param val número inteiro a ser transformado
     * @return array de byte correspondente
     */
    private byte[] intToByte(int val) {
        byte[] array = {0, 0, 0, 0};
        for (int i = 0; i < 4; i++) {
            array[i] = (byte) val;
            val >>= 8;
        }
        return array;
    }

    /**
     * transforma parte de um array de bytes em números inteiros
     * * @param offSet indice inicial do array a ser transformado
     * @param array o array que contêm o n. inteiro
     * @return número inteiro
     * @throws ArrayIndexOutOfBoundsException - Caso o array não possua offSet + 4 elementos
     */
    private int byteToInt(int offSet, byte[] array) throws ArrayIndexOutOfBoundsException {


        int val = 0;
        for (int i = 3; i >= 0; i--) {
            val <<= 8;
            val |= (0xff & array[i + offSet]); //Faz um ou lógico bit a bit depoi de passalo por uma mascára que pega um int com os oito bits procurados
        }
        return val;
    }
    /*  public static void main(String a[]){

    try{
    TrafficDatagramPacket tdp = new TrafficDatagramPacket(InetAddress.getByName("127.0.0.1"),5000,1000,1);
    byte array[] = new byte [tdp.data.length];
    SyncTimestamp tsAux = new SyncTimestamp(0,tdp.timestamp.getTimestampData());
    for(int i = 0; i<array.length; i++)
    array[i] = tdp.data[i];
    tdp = new TrafficDatagramPacket(new DatagramPacket(array,tdp.getData().length,InetAddress.getByName("127.0.0.1"),5000 )  );

    System.out.println("Tempo Dele mesmo: "+tsAux.getTimeInMilliSeconds());
    System.out.println("Tempo: "+ tdp.getTimestamp().getTimeInMilliSeconds()+"\nNúmero: "+tdp.getPacketNum());
    }catch(Exception e){
    e.printStackTrace();
    }
    }
     */
}
